#include "stdafx.h"

#include <windows.h>
#include "File.h"

using namespace WCMM;

BOOL CFile::Create(
	_In_z_ LPCTSTR szFilename,
	_In_ DWORD dwDesiredAccess,
	_In_ DWORD dwShareMode,
	_In_ DWORD dwCreationDisposition,
	_In_opt_ DWORD dwFlagsAndAttributes,
	_In_opt_ LPSECURITY_ATTRIBUTES lpsa,
	_In_opt_ HANDLE hTemplateFile) throw()
{
	ASSERTE(m_h == NULL);

	HANDLE hFile = ::CreateFile(
		szFilename,
		dwDesiredAccess,
		dwShareMode,
		lpsa,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile);

	if (hFile == INVALID_HANDLE_VALUE)
		return FALSE;

	Attach(hFile);
	return TRUE;
}

BOOL CFile::GetSize(_Out_ ULONGLONG& nLen) const throw()
{
	ASSERTE(m_h != NULL);

	ULARGE_INTEGER liFileSize;
	liFileSize.LowPart = ::GetFileSize(m_h, &liFileSize.HighPart);
	if (liFileSize.LowPart == INVALID_FILE_SIZE){
		return FALSE;
	}

	nLen = liFileSize.QuadPart;

	return TRUE;
}

BOOL CFile::SetSize(_In_ ULONGLONG nNewLen) throw()
{
	ASSERTE(m_h != NULL);

	BOOL bRet = Seek(nNewLen, FILE_BEGIN);
	if (!bRet)
		return bRet;

	return ::SetEndOfFile(m_h);
}

BOOL CFile::Seek(
	_In_ LONGLONG nOffset,
	_In_ DWORD dwFrom) throw()
{
	ASSERTE(m_h != NULL);
	//ASSERTE(dwFrom == FILE_BEGIN || dwFrom == FILE_END || dwFrom == FILE_CURRENT);

	LARGE_INTEGER liOffset;
	liOffset.QuadPart = nOffset;
	DWORD nNewPos = ::SetFilePointer(m_h, liOffset.LowPart, &liOffset.HighPart, dwFrom);
	if (nNewPos == INVALID_SET_FILE_POINTER){
		return FALSE;
	}

	return TRUE;
}

BOOL CFile::Read(
	_Out_writes_bytes_(nBufSize) LPVOID pBuffer,
	_In_ DWORD nBufSize) throw()
{
	ASSERTE(m_h != NULL);

	DWORD nBytesRead = 0;
	return ::ReadFile(m_h, pBuffer, nBufSize, &nBytesRead, NULL);

}

BOOL CFile::Read(
	_Out_writes_bytes_(nBufSize) LPVOID pBuffer,
	_In_ DWORD nBufSize,
	_Out_ DWORD& nBytesRead) throw()
{
	ASSERTE(m_h != NULL);

	return ::ReadFile(m_h, pBuffer, nBufSize, &nBytesRead, NULL);
}

//
// this function will usually return HRESULT_FROM_WIN32(ERROR_IO_PENDING)
// indicating succesful queueing of the operation
//

BOOL CFile::Read(
	_Out_writes_bytes_(nBufSize) LPVOID pBuffer,
	_In_ DWORD nBufSize,
	_Inout_opt_ LPOVERLAPPED pOverlapped) throw()
{
	ASSERTE(m_h != NULL);

	return ::ReadFile(m_h, pBuffer, nBufSize, NULL, pOverlapped);
}

BOOL CFile::Read(
	_Out_writes_bytes_(nBufSize) LPVOID pBuffer,
	_In_ DWORD nBufSize,
	_Inout_ LPOVERLAPPED pOverlapped,
	_In_ LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine) throw()
{
	ASSERTE(m_h != NULL);

	return ::ReadFileEx(m_h, pBuffer, nBufSize, pOverlapped, pfnCompletionRoutine);
}

BOOL CFile::Write(
	_In_reads_bytes_(nBufSize) LPCVOID pBuffer,
	_In_ DWORD nBufSize,
	_Out_opt_ DWORD* pnBytesWritten) throw()
{
	ASSERTE(m_h != NULL);

	DWORD nBytesWritten;
	if (pnBytesWritten == NULL)
		pnBytesWritten = &nBytesWritten;
	return ::WriteFile(m_h, pBuffer, nBufSize, pnBytesWritten, NULL);
}

//
// this function will usually return HRESULT_FROM_WIN32(ERROR_IO_PENDING)
// indicating succesful queueing of the operation
//

BOOL CFile::Write(
	_In_reads_bytes_(nBufSize) LPCVOID pBuffer,
	_In_ DWORD nBufSize,
	_Inout_opt_ LPOVERLAPPED pOverlapped) throw()
{
	ASSERTE(m_h != NULL);

	return ::WriteFile(m_h, pBuffer, nBufSize, NULL, pOverlapped);
}

BOOL CFile::Write(
	_In_reads_bytes_(nBufSize) LPCVOID pBuffer,
	_In_ DWORD nBufSize,
	_Inout_ LPOVERLAPPED pOverlapped,
	_In_opt_ LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine) throw()
{
	ASSERTE(m_h != NULL);

	return::WriteFileEx(m_h, pBuffer, nBufSize, pOverlapped, pfnCompletionRoutine);
}

//
// this function returns HRESULT_FROM_WIN32(ERROR_IO_INCOMPLETE)
// if bWait is false and the operation is still pending
//

BOOL CFile::GetOverlappedResult(
	_In_ LPOVERLAPPED pOverlapped,
	_Out_ DWORD& dwBytesTransferred,
	_In_ BOOL bWait) throw()
{
	return ::GetOverlappedResult(m_h, pOverlapped, &dwBytesTransferred, bWait);
}


BOOL CFile::GetPosition(_Out_ ULONGLONG& nPos) const throw()
{
	ASSERTE(m_h != NULL);

	LARGE_INTEGER liOffset;
	liOffset.QuadPart = 0;
	liOffset.LowPart = ::SetFilePointer(m_h, 0, &liOffset.HighPart, FILE_CURRENT);
	if (liOffset.LowPart == INVALID_SET_FILE_POINTER){
		return FALSE;
	}
	nPos = liOffset.QuadPart;

	return TRUE;
}

BOOL CFile::Flush() throw()
{
	ASSERTE(m_h != NULL);

	return ::FlushFileBuffers(m_h);
}

BOOL CFile::LockRange(
	_In_ ULONGLONG nPos,
	_In_ ULONGLONG nCount) throw()
{
	ASSERTE(m_h != NULL);

	LARGE_INTEGER liPos;
	liPos.QuadPart = nPos;

	LARGE_INTEGER liCount;
	liCount.QuadPart = nCount;

	return ::LockFile(m_h, liPos.LowPart, liPos.HighPart, liCount.LowPart, liCount.HighPart);
}

BOOL CFile::UnlockRange(
	_In_ ULONGLONG nPos,
	_In_ ULONGLONG nCount) throw()
{
	ASSERTE(m_h != NULL);

	LARGE_INTEGER liPos;
	liPos.QuadPart = nPos;

	LARGE_INTEGER liCount;
	liCount.QuadPart = nCount;

	return ::UnlockFile(m_h, liPos.LowPart, liPos.HighPart, liCount.LowPart, liCount.HighPart);

}

BOOL CTemporaryFile::Create(
	_In_opt_z_ LPCTSTR pszDir,
	_In_ DWORD dwDesiredAccess) throw()
{
	TCHAR szPath[MAX_PATH];
	TCHAR tmpFileName[MAX_PATH];

	ASSERTE(m_h == NULL);

	if (pszDir == NULL){
		DWORD dwRet = GetTempPath(_MAX_DIR, szPath);
		if (dwRet == 0){

			//
			// Couldn't find temporary path;
			//

			return FALSE;
		}else if (dwRet > _MAX_DIR){
			return FALSE;
		}
	}else{
		if(!lstrcpyn(szPath, pszDir, _countof(szPath))){
			return FALSE;
		}
	}
		
	//
	// create temporary filename;
	//
		
	if (!GetTempFileName(szPath, _T("TFR"), 0, tmpFileName)){
			
		//
		// TODO AtlHresultFromLastError
		//
			
		return FALSE;
	}

	tmpFileName[_countof(tmpFileName)-1]='\0';

	lstrcpyn(m_szTempFileName, tmpFileName, _countof(m_szTempFileName));

	SECURITY_ATTRIBUTES secatt;
	secatt.nLength = sizeof(secatt);
	secatt.lpSecurityDescriptor = NULL;
	secatt.bInheritHandle = TRUE;

	m_dwAccess = dwDesiredAccess;

	return CFile::Create(
		m_szTempFileName,
		m_dwAccess,
		0,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY,
		&secatt);
}

void CTemporaryFile::Close() throw()
{
	Close(NULL);
}

BOOL CTemporaryFile::Close(_In_opt_z_ LPCTSTR szNewName) throw()
{
	ASSERTE(m_h != NULL);

	//
	// This routine is called when we are finished writing to the
	// temporary file, so we now just want to close it and copy
	// it to the actual filename we want it to be called.
	//

	//
	// So let's close it first.
	//

	CFile::Close();

	//
	// no new name so delete it
	//

	if (szNewName == NULL){
		::DeleteFile(m_szTempFileName);
		return TRUE;
	}

	//
	// delete any existing file and move our temp file into it's place
	//

	if (!::DeleteFile(szNewName)){
		DWORD dwError = GetLastError();
		if (dwError != ERROR_FILE_NOT_FOUND){
				
			//
			// TODO AtlHresultFromLastError
			//
				
			return FALSE;
		}

	}

	if (!::MoveFile(m_szTempFileName, szNewName)){

		//
		// TODO AtlHresultFromLastError
		//

		return FALSE;
	}

	return TRUE;
}

	
/** \fn 
	\brief Call this method to disassociate the file from the CAtlTemporaryFile object.
	\return 
*/
	
BOOL CTemporaryFile::HandsOff() throw()
{
	Flush();
	Close();

	return TRUE;
}
	
/** \fn 
	\brief Call this method to open an existing temporary file and position the pointer at the end of the file.
	\return 
*/
	
BOOL CTemporaryFile::HandsOn() throw()
{
	if(!CFile::Create(
		m_szTempFileName,
		m_dwAccess,
		0,
		OPEN_EXISTING))
		return FALSE;
	return Seek(0, FILE_END);
}


LPCTSTR CTemporaryFile::TempFileName() throw()
{
	return m_szTempFileName;
}


CFileMappingBase::CFileMappingBase() throw()
{
	m_pData = NULL;
	m_hMapping = NULL;
}

CFileMappingBase::~CFileMappingBase() throw()
{
	Unmap();
}

BOOL CFileMappingBase::MapFile(
	_In_ HANDLE hFile,
	_In_ SIZE_T nMappingSize,
	_In_ ULONGLONG nOffset,
	_In_ DWORD dwMappingProtection,
	_In_ DWORD dwViewDesiredAccess) throw()
{
	ASSERTE(m_pData == NULL);
	ASSERTE(m_hMapping == NULL);
	ASSERTE(hFile != INVALID_HANDLE_VALUE && hFile != NULL);

	ULARGE_INTEGER liFileSize;
	liFileSize.LowPart = ::GetFileSize(hFile, &liFileSize.HighPart);
	if (liFileSize.QuadPart < nMappingSize)
		liFileSize.QuadPart = nMappingSize;

	m_hMapping = ::CreateFileMapping(hFile, NULL, dwMappingProtection, liFileSize.HighPart, liFileSize.LowPart, 0);
	if (m_hMapping == NULL){

		//
		// TODO AtlHresultFromLastError();
		//

		return FALSE;
	}


	if (nMappingSize == 0)
		m_nMappingSize = (SIZE_T) (liFileSize.QuadPart - nOffset);
	else
		m_nMappingSize = nMappingSize;

	m_dwViewDesiredAccess = dwViewDesiredAccess;
	m_nOffset.QuadPart = nOffset;

	m_pData = ::MapViewOfFileEx(m_hMapping, m_dwViewDesiredAccess, m_nOffset.HighPart, m_nOffset.LowPart, m_nMappingSize, NULL);
	if (m_pData == NULL){

		//
		// TODO AtlHresultFromLastError();
		//

		::CloseHandle(m_hMapping);
		m_hMapping = NULL;
		return FALSE;
	}

	return TRUE;
}

BOOL CFileMappingBase::MapSharedMem(
	_In_ SIZE_T nMappingSize,
	_In_z_ LPCTSTR szName,
	_Out_opt_ BOOL* pbAlreadyExisted,
	_In_opt_ LPSECURITY_ATTRIBUTES lpsa,
	_In_ DWORD dwMappingProtection,
	_In_ DWORD dwViewDesiredAccess) throw()
{
	ASSERTE(m_pData == NULL);
	ASSERTE(m_hMapping == NULL);
	ASSERTE(nMappingSize > 0);
	//ASSERTE(szName != NULL); // if you just want a regular chunk of memory, use a heap allocator

	m_nMappingSize = nMappingSize;

	ULARGE_INTEGER nSize;
	nSize.QuadPart = nMappingSize;
	m_hMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, lpsa, dwMappingProtection, nSize.HighPart, nSize.LowPart, szName);
	if (m_hMapping == NULL){

		//
		// TODO AtlHresultFromLastError();
		//

		return FALSE;
	}

	if (pbAlreadyExisted != NULL)
		*pbAlreadyExisted = (GetLastError() == ERROR_ALREADY_EXISTS);

	m_dwViewDesiredAccess = dwViewDesiredAccess;
	m_nOffset.QuadPart = 0;

	m_pData = ::MapViewOfFileEx(m_hMapping, m_dwViewDesiredAccess, m_nOffset.HighPart, m_nOffset.LowPart, m_nMappingSize, NULL);
	if (m_pData == NULL){

		//
		// TODO AtlHresultFromLastError();
		//

		::CloseHandle(m_hMapping);
		m_hMapping = NULL;
		return FALSE;
	}


	return TRUE;
}

BOOL CFileMappingBase::OpenMapping(
	_In_z_ LPCTSTR szName,
	_In_ SIZE_T nMappingSize,
	_In_ ULONGLONG nOffset,
	_In_ DWORD dwViewDesiredAccess) throw()
{
	ASSERTE(m_pData == NULL);
	ASSERTE(m_hMapping == NULL);
	//ATLASSERT(szName != NULL); // if you just want a regular chunk of memory, use a heap allocator

	m_nMappingSize = nMappingSize;
	m_dwViewDesiredAccess = dwViewDesiredAccess;

	m_hMapping = ::OpenFileMapping(m_dwViewDesiredAccess, FALSE, szName);
	if (m_hMapping == NULL){

		//
		// TODO AtlHresultFromLastError()
		//

		return FALSE;
	}

	m_dwViewDesiredAccess = dwViewDesiredAccess;
	m_nOffset.QuadPart = nOffset;

	m_pData = ::MapViewOfFileEx(m_hMapping, m_dwViewDesiredAccess, m_nOffset.HighPart, m_nOffset.LowPart, m_nMappingSize, NULL);
	if (m_pData == NULL){

		//
		// TODO AtlHresultFromLastError();
		//

		::CloseHandle(m_hMapping);
		m_hMapping = NULL;
		return FALSE;
	}

	return TRUE;
}

BOOL CFileMappingBase::Unmap() throw()
{
	BOOL bRet = TRUE;

	if (m_pData != NULL){
		if (!::UnmapViewOfFile(m_pData))
			bRet = FALSE;
		m_pData = NULL;
	}

	if (m_hMapping != NULL){
		if (!::CloseHandle(m_hMapping) && bRet)
			bRet = FALSE;
		m_hMapping = NULL;
	}
	return bRet;
}

void* CFileMappingBase::GetData() const throw()
{
	return m_pData;
}

HANDLE CFileMappingBase::GetHandle() const throw()
{
	return m_hMapping;
}

SIZE_T CFileMappingBase::GetMappingSize() throw()
{
	return m_nMappingSize;
}

BOOL CFileMappingBase::CopyFrom(_In_ CFileMappingBase& OrgMap) throw()
{
	if (this == &OrgMap)
		return TRUE;

	ASSERTE(m_pData == NULL);
	ASSERTE(m_hMapping == NULL);
	ASSERTE(OrgMap.m_pData != NULL);

	if (!OrgMap.m_hMapping){
		return FALSE;
	}

	m_dwViewDesiredAccess = OrgMap.m_dwViewDesiredAccess;
	m_nOffset.QuadPart = OrgMap.m_nOffset.QuadPart;
	m_nMappingSize = OrgMap.m_nMappingSize;

	if (!::DuplicateHandle(GetCurrentProcess(), OrgMap.m_hMapping, GetCurrentProcess(),
		&m_hMapping, NULL, TRUE, DUPLICATE_SAME_ACCESS)){

			//
			// TODO AtlHresultFromLastError()
			//

			return FALSE;
	}

	m_pData = ::MapViewOfFileEx(m_hMapping, m_dwViewDesiredAccess, m_nOffset.HighPart, m_nOffset.LowPart, m_nMappingSize, NULL);
	if (m_pData == NULL){

		//
		// TODO AtlHresultFromLastError()
		//

		::CloseHandle(m_hMapping);
		m_hMapping = NULL;
		return FALSE;
	}

	return TRUE;
}

CFileMappingBase::CFileMappingBase(_In_ CFileMappingBase& OrgMap)
{
	m_pData = NULL;
	m_hMapping = NULL;

	BOOL bRet = CopyFrom(OrgMap);
	if (!bRet)
		throw GetLastError();

}

CFileMappingBase& CFileMappingBase::operator=(_In_ CFileMappingBase& OrgMap)
{
	BOOL bRet = CopyFrom(OrgMap);
	if (!bRet)
		throw GetLastError();

	return *this;
}